#ifndef SC_LOG_H
#define SC_LOG_H

/*
日志功能
*/

#include "common.h"

#include <SDL2/SDL_log.h>

#include "options.h"

/*
（1） 简单的宏定义
    ①宏名一般用大写
    ②宏定义末尾不加分好;
    ③可以用#undef命令终止宏定义的作用域
    ④宏定义可以嵌套
    ⑤字符串“”中永远不包含宏
    ⑥宏替换在编译前进行，不分配内存，变量定义分配内存，函数调用在编译后程序运行时进行，并且分配内存
    ⑦预处理是在编译之前的处理，而编译工作的任务之一就是语法检查，预处理不做语法检查
    ⑧使用宏可提高程序的通用性和易读性，减少不一致性，减少输入错误和便于修改。例如：数组大小常用宏定义

（2） 带参数的宏定义（除了一般的字符串替换，还要做参数代换）
    格式：#define <宏名>(<参数表>) <字符串>
    eg:#define S(a,b) a*b
        area=S(3,2);

        第一步被换为area=a*b;第二步换为area=3*2;
    
    一个标识符被宏定义后，该标识符便是一个宏名。这时，在程序中出现的是宏名，在该程序被编译前，先将宏名用被定义的字符串替换，这称为宏替换，替换后才进行编译，宏替换是简单的替换。

说明：

①实参如果是表达式容易出问题

#define S(r) r*r

area=S(a+b);第一步换为area=r*r;第二步换成area=a+b*a+b;

当定义为#define S(r)((r)*(r))时area=((a+b)*(a+b))

②宏名和参数的括号间不能有空格
③宏替换之作替换不做计算，不做表达式求解
④宏的哑实结合不存在类型，也没有类型转换
⑤宏展开不占用运行时间，只占用编译时间，函数调用占运行时间（分配内存、保留现场、值传递、返回值）    

参考链接： https://zhuanlan.zhihu.com/p/367761694

gcc -save-temps就可以将中间结果的产生文件全部显示。
*/

// #x是给x加双引号
#define LOG_STR_IMPL_(x) # x
#define LOG_STR(x) LOG_STR_IMPL_(x)

// SDL支持针对不同的日志类别设置日志级别，SDL自己抽象出来.比如，可以设置VIDEO的info级别日志进行输出，但是AUDIO的info级别不输出。

/*
SDL日志具有下列分类
    SDL_LOG_CATEGORY_APPLICATION
    SDL_LOG_CATEGORY_ERROR
    SDL_LOG_CATEGORY_ASSERT
    SDL_LOG_CATEGORY_SYSTEM
    SDL_LOG_CATEGORY_AUDIO
    SDL_LOG_CATEGORY_VIDEO
    SDL_LOG_CATEGORY_RENDER
    SDL_LOG_CATEGORY_INPUT
    SDL_LOG_CATEGORY_TEST

SDL日志具有下列优选级:
    SDL_LOG_PRIORITY_VERBOSE(低优先级)
    SDL_LOG_PRIORITY_DEBUG
    SDL_LOG_PRIORITY_INFO
    SDL_LOG_PRIORITY_WARN
    SDL_LOG_PRIORITY_ERROR
    SDL_LOG_PRIORITY_CRITICAL
    SDL_NUM_LOG_PRIORITIES(高优先级)


SDL日志的输出也可以被重新定向，你可以使用下面的函数来取得和设置输出函数。

    void SDL_LogGetOutputFunction(SDL_LogOutputFunction *callback, void **userdata);
    void SDL_LogSetOutputFunction(SDL_LogOutputFunction callback, void *userdata);

原文链接：https://blog.csdn.net/john_crash/article/details/46991435
*/

// SDL_LogVerbose 优先级为SDL_LOG_PRIORITY_VERBOSE
#define LOGV(...) SDL_LogVerbose(SDL_LOG_CATEGORY_APPLICATION, __VA_ARGS__)

// __VA_ARGS__:用于在宏替换部分中，表示可变参数列表；
// /* SDL_LogVerbose 优先级为SDL_LOG_PRIORITY_DEBUG */
#define LOGD(...) SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, __VA_ARGS__)
#define LOGI(...) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, __VA_ARGS__)
#define LOGW(...) SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, __VA_ARGS__)
#define LOGE(...) SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, __VA_ARGS__)

// Out of Memory 内存溢出打印错误日志。注意下面的几个宏定义的使用
#define LOG_OOM() \
    LOGE("OOM: %s:%d %s()", __FILE__, __LINE__, __func__)


// 用于调试期间的输出更多的内容,每次打印内容前主动打印当前代码的位置.不用LOGI是因为这个函数会在每条日志前打印一个级别信息
# define JACK_INFO_PRINT() printf("\n------- %s -- %d : %s() -------\n", __FILE__, __LINE__, __func__);

/**
 * 设置 scrcpy 日志级别
 * @param level 日志级别
*/
void
sc_set_log_level(enum sc_log_level level);

/* 获取自定义的日志级别 */
enum sc_log_level
sc_get_log_level(void);

/// @brief 专用于adb 或 usb 设备的日志输出
/// @param  日志级别
/// @param fmt 模式字符串。如%s 参考printf用法
/// @param  动态替换变量
void
sc_log(enum sc_log_level level, const char *fmt, ...);
#define LOG(LEVEL, ...) sc_log((LEVEL), __VA_ARGS__)

#ifdef _WIN32
// Log system error (typically returned by GetLastError() or similar)
bool
sc_log_windows_error(const char *prefix, int error);
#endif

/* 日志系统配置 */
void
sc_log_configure(void);

#endif
